#-------------------------------------------------------------------------------
# PushConstrainOp.py: This is a Python script which creates an operator 
# that constrains the kinematic global position of a 3D object (the Passive) 
# to another (the Force). The constrained degree of freedom can be controlled
# through the PushConstrainOp.
#-------------------------------------------------------------------------------

from win32com.client import constants
from win32com.client.dynamic import Dispatch 
import pywin.debugger
import win32traceutil

#-------------------------------------------------------------------------------
# XSILoadPlugin: Entry-point called by XSI when this file gets loaded. All 
# plugin items exposed here must be registered in this function. 
#
# in_reg: PluginRegistrar object required for registering the plugin 
# items and some information about this plugin.
#
# *note* This function is mandatory and must be implemented. The plugin will 
# not be loaded if this function is missing.
#-------------------------------------------------------------------------------

def XSILoadPlugin( in_reg ):

	# register plugin information
	in_reg.Author = "Unitlog 3DCG" ;
	in_reg.Name = "Push Constrain Operator plug-in";

	# set the version number of this plugin
	in_reg.Major = 1;
	in_reg.Minor = 2;

	# register the custom operator
	in_reg.RegisterOperator("PushConstrainOp");

	# register the command to connect the PushConstrainOp operator
	in_reg.RegisterCommand("CreatePushConstrainOp");		
	
	# RegistrationInsertionPoint - do not remove this line

	strPluginName = in_reg.Name
	Application.LogMessage(str(strPluginName) + str(" has been loaded."),constants.siVerbose)

	return True;	


def CreatePushConstrainOp_Init( ctxt ):

	oCmd = ctxt.Source;
	oCmd.Description = "Create an instance of PushConstrainOp operator";
	oCmd.ReturnValue = True

	#XSIUtils.ExternalScriptDebugger = 1

	return True


def CreatePushConstrainOp_Execute( ):

	Application.LogMessage("CreatePushConstrainOp_Execute called",constants.siVerbose )

	oForce = Application.PickElement( constants.siGenericObjectFilter, 'Pick Force', 'Pick Force' )
	oController = oForce.Value("PickedElement")

	oPassive = Application.PickElement( constants.siGenericObjectFilter, 'Pick Passive', 'Pick Passive' )
	oConstrained = oPassive.Value("PickedElement")
	
	# Create/connect the PushConstrainOp Operator             
	ConnectToPushConstrainOp( oController, oConstrained)

	return True


#-------------------------------------------------------------------------------
# ConnectToPushConstrainOp: Connect objects to Push constrain operator
#-------------------------------------------------------------------------------
def ConnectToPushConstrainOp( in_oController, in_oConstrained ):
	    
	oPushOp = XSIFactory.CreateObject("PushConstrainOp")

	oGroup = oPushOp.AddPortGroup( "Target" )

	oPushOp.AddOutputPort( in_oConstrained.Kinematics.Global, "Constrained", oGroup.Index )

	oGroup1 = oPushOp.AddPortGroup( "RefPush" )

	oPushOp.AddInputPort( in_oConstrained.Kinematics.Global, "FreeConstrainted", oGroup1.Index)

	oGroup2 = oPushOp.AddPortGroup( "Controller" )

	oGlobal = in_oController.Kinematics.Global
	oPushOp.AddInputPort( oGlobal.posx, "gNx", oGroup2.Index )
	oPushOp.AddInputPort( oGlobal.posy, "gNy", oGroup2.Index )
	oPushOp.AddInputPort( oGlobal.posz, "gNz", oGroup2.Index )

	oPassive = in_oConstrained.Kinematics.Global
	oPushOp.AddInputPort( oPassive.posx, "pNx", oGroup1.Index )
	oPushOp.AddInputPort( oPassive.posy, "pNy", oGroup1.Index )
	oPushOp.AddInputPort( oPassive.posz, "pNz", oGroup1.Index )

	
       
	oPushOp.Connect()


	

#-------------------------------------------------------------------------------
# PushConstrainOp_Init: Store math objects in the operator context to maximize
# performance
#-------------------------------------------------------------------------------
def PushConstrainOp_Init( in_ctxt ):
	
	oDict = Dispatch( "Scripting.Dictionary" )

	oDict[ 'PushPos' ] = XSIMath.CreateVector3()
	oDict[ 'OutPos' ] = XSIMath.CreateVector3()

	in_ctxt.UserData = oDict;
	
	return True
	

#-------------------------------------------------------------------------------
# PushConstrainOp_Update
#-------------------------------------------------------------------------------
def PushConstrainOp_Update( in_ctxt ):
	oDict = in_ctxt.UserData
	oKineState = in_ctxt.OutputTarget
	
	oGlobalTrans = oKineState.GetTransform2(None)

	gNx = in_ctxt.GetInputValue("gNx")
	gNy = in_ctxt.GetInputValue("gNy")
	gNz = in_ctxt.GetInputValue("gNz")

	pNx = in_ctxt.GetInputValue("pNx")
	pNy = in_ctxt.GetInputValue("pNy")
	pNz = in_ctxt.GetInputValue("pNz")
	
	
	oPushPos = oDict[ 'PushPos' ]
	oPushPos.Set( gNx, gNy, gNz )

	oPushOp = in_ctxt.Source
	pushX = oPushOp.Parameters('X').Value
	pushY = oPushOp.Parameters('Y').Value
	pushZ = oPushOp.Parameters('Z').Value
	pushNegX = oPushOp.Parameters('-X').Value
	pushNegY = oPushOp.Parameters('-Y').Value
	pushNegZ = oPushOp.Parameters('-Z').Value

	oOutPos = oDict[ 'OutPos' ]
	posx = pNx;
	posy = pNy;
	posz = pNz;

	if pushX and gNx > pNx:
		posx = gNx;
	if pushY and gNy > pNy:
		posy = gNy;
	if pushZ and gNz > pNz:
		posz = gNz;
	if pushNegX and gNx < pNx:
		posx = gNx;
	if pushNegY and gNy < pNy:
		posy = gNy;
	if pushNegZ and gNz < pNz:
		posz = gNz;

	oOutPos.Set( posx, posy, posz);

	#pywin.debugger.brk()
	#Application.LogMessage(str("pushX=")+str(pushX)+str(" pushNegX=")+str(pushNegX))
	#Application.LogMessage(str("posx=")+str(posx)+str(" pNx=")+str(pNx)+str(" gNx=")+str(gNx))
		
	oGlobalTrans.SetTranslation( oOutPos )
	oKineState.PutTransform2( None, oGlobalTrans )

	return True

#-------------------------------------------------------------------------------
# PushConstrainOp_Define
#-------------------------------------------------------------------------------
def PushConstrainOp_Define( in_ctxt ):

	# Source returns a CustomProperty object in the context of a "Define" callback
	oPushOp = in_ctxt.Source;


	paramdef1 = XSIFactory.CreateParamDef2( "X", constants.siBool, 0)
	paramdef2 = XSIFactory.CreateParamDef2( "Y", constants.siBool, 0)
	paramdef3 = XSIFactory.CreateParamDef2( "Z", constants.siBool, 0)
	paramdef4 = XSIFactory.CreateParamDef2( "-X", constants.siBool, 0)
	paramdef5 = XSIFactory.CreateParamDef2( "-Y", constants.siBool, 0)
	paramdef6 = XSIFactory.CreateParamDef2( "-Z", constants.siBool, 0)

	oParam = oPushOp.AddParameter( paramdef1 );
	oParam = oPushOp.AddParameter( paramdef2 );
	oParam = oPushOp.AddParameter( paramdef3 );
	oParam = oPushOp.AddParameter( paramdef4 );
	oParam = oPushOp.AddParameter( paramdef5 );
	oParam = oPushOp.AddParameter( paramdef6 );


#-------------------------------------------------------------------------------
# PushConstrainOp_DefineLayout
#-------------------------------------------------------------------------------
def PushConstrainOp_DefineLayout( in_ctxt ):

	# Source returns a PPGLayout object in the context of a "DefineLayout" callback
	layout = in_ctxt.Source;
	layout.Clear();

	layout.AddGroup( "Degree of Freedom")
	layout.AddItem("X");
	layout.AddItem("Y");
	layout.AddItem("Z");
	layout.AddItem("-X");
	layout.AddItem("-Y");
	layout.AddItem("-Z");
	layout.EndGroup();

